/*
 * Exploder
 * 
 * Tero Pyylampi (218437), Jussi Tuurinkoski (211594)
 *
 * Main game layer
 */

#include "LayerGame.h"
#include "../GameScene.h"
#include "../GameEngine.h"
#include "../GameCamera.h"
#include "../misc/GameTouchHandle.h"
#include "../layers/LayerInterface.h"
#include "../interface/GameInterfaceEngine.h"
#include "../interface/GameInterfaceGameContainer.h"
#include "../interface/GameInterfacePhysicObjectButton.h"
#include "../objects/physicobjects/bombs/GameBomb.h"
#include "../objects/physicobjects/GamePhysicObjectEngine.h"
#include "../objects/physicobjects/GamePhysicObject.h"
#include "../../MainEngine.h"
#include "../../Common.h"
#include "../../misc/UserData.h"

// default constructor
LayerGame::LayerGame( GameScene *scene, GameCamera *camera ) : mGameScene( scene ), mGameCamera( camera ), mTouchHandle( 0 ) {
	initializeLayer();
}

// default destructor
LayerGame::~LayerGame() {
	// TODO
}

// initialize this scene
void LayerGame::initializeLayer() {
	// create game engine
	mGameEngine = new GameEngine( mGameScene, mGameCamera, this );

	// init touch handle
	b2BodyDef groundBodyDef;
	b2Body *groundBody = mGameEngine->getGameWorld()->CreateBody( &groundBodyDef );
	mTouchHandle = new GameTouchHandle( mGameCamera, groundBody );

	// allow input
	setIsTouchEnabled( true );
}

// update -function
void LayerGame::update( cocos2d::ccTime dt ) {
	// update game engine
	mGameEngine->update( dt );

	// check whether we are carrying a mine over a construction element and switch it's color
	if( mTouchHandle->getCurrentType() == TOUCH_PHYSICS_OBJECT ) {
		GamePhysicObject *obj = mGameEngine->getPhysicObjectEngine()->findOwnerOfBody( mTouchHandle->getHitBody() );
		// check for collisions
		if( mGameEngine->getPhysicObjectEngine()->checkIfObjectCollidesWithAnything( mTouchHandle->getHitBody() )
			|| obj->getObjectScreenPosition().x > WIN_WIDTH * 0.82f ) {
			cocos2d::ccColor3B c = { 255, 0, 0 };
			obj->setObjectColor( c );
			mTouchHandle->setInvalidBombPosition( true );
		}
		else {
			cocos2d::ccColor3B c = { 255, 255, 255 };
			obj->setObjectColor( c );
			mTouchHandle->setInvalidBombPosition( false );
		}
	}
}
	
// called when the user puts the finger on the screen
void LayerGame::ccTouchesBegan( cocos2d::CCSet *touches, cocos2d::CCEvent *event ) {
	// skip events if the game is not running normally
	if( mGameEngine->getCurrentGameState() != GAME_RUNNING )
		return;

	cocos2d::CCTouch *touch = dynamic_cast< cocos2d::CCTouch* >( touches->anyObject() );

	// convert to screen coordinates
	cocos2d::CCPoint pos = touch->locationInView();
	pos = cocos2d::CCDirector::sharedDirector()->convertToGL( pos );
	// get world coordinates
	cocos2d::CCPoint absoluteScreenPos = this->convertToNodeSpace( pos );
	b2Vec2 worldLoc = mGameCamera->convertScreenToWorld( b2Vec2( absoluteScreenPos.x, absoluteScreenPos.y ) );

	// check for interface button presses
	b2Body *resultBody = checkForInterfacePhysicObjectButtonPresses( pos, worldLoc );
	if( resultBody != 0 ) {
		mTouchHandle->startObjectTouch( touch, this, resultBody, mGameEngine->getGameWorld() );
		return;
	}
	
	// check for existing objects
	GamePhysicObject *obj = checkForExistingPhysicObjects( pos, worldLoc );
	if( obj != 0 ) {
		mTouchHandle->startObjectTouch( touch, this, obj->getBodyObjects()->at( 0 ), mGameEngine->getGameWorld() );
		return;
	}

	// nothing found, start camera touch
	mTouchHandle->startMovingCamera( touch, this );
}

// called when the finger moves on the screen
void LayerGame::ccTouchesMoved( cocos2d::CCSet *touches, cocos2d::CCEvent *event ) {
	cocos2d::CCTouch *touch = dynamic_cast< cocos2d::CCTouch* >( touches->anyObject() );
	mTouchHandle->update( touch, this );
}

// called when the user lifts the finger from the screen
void LayerGame::ccTouchesEnded( cocos2d::CCSet *touches, cocos2d::CCEvent *event ) {
	cocos2d::CCTouch *touch = dynamic_cast< cocos2d::CCTouch* >( touches->anyObject() );

	// check whether to remove current bomb as a result of invalid position
	if( mTouchHandle->getCurrentType() == TOUCH_PHYSICS_OBJECT && mTouchHandle->getInvalidBombPosition() == true ) {
		b2Body *hitBody = mTouchHandle->getHitBody();
		mTouchHandle->reset();

		// get the bomb object and force removal
		GamePhysicObject *bomb = mGameEngine->getPhysicObjectEngine()->findOwnerOfBody( hitBody );
		bomb->forceRemoveFromGame();
		
		// reward player with half the price
		mGameEngine->modifyCurrentMoney( static_cast< int >( PRIZE_ARRAY[ bomb->getObjectType() ] / 2.0f ) );
	}
	// normally reset state
	else
		mTouchHandle->reset();
}

// get master
GameScene *LayerGame::getGameScene() {
	return mGameScene;
}

// get game engine
GameEngine *LayerGame::getGameEngine() {
	return mGameEngine;
}

// get touch handle
GameTouchHandle *LayerGame::getTouchHandle() {
	return mTouchHandle;
}

// check for interface physic object button presses
b2Body *LayerGame::checkForInterfacePhysicObjectButtonPresses( cocos2d::CCPoint &screenCoords, b2Vec2 &worldCoords ) {
	std::vector< GameInterfacePhysicObjectButton *> container = mGameScene->getInterfaceLayer()->getInterfaceEngine()->getGameContainer()->getObjectButtons();
	for( unsigned int i = 0; i < container.size(); ++i ) {
		if( container.at( i )->testIfCoordinatesAreInsideBackgroundSprite( screenCoords ) && mGameEngine->getCurrentMoney() >= container.at( i )->getObjectPrice() ) {
			mGameEngine->modifyCurrentMoney( -container.at( i )->getObjectPrice() );
			return container.at( i )->createAndInitializeNewCorrespondingItem( mGameCamera, worldCoords );
		}
	}

	// nothing found
	return 0;
}

// check for existing bomb presses
GamePhysicObject *LayerGame::checkForExistingPhysicObjects( cocos2d::CCPoint &screenCoords, b2Vec2 &worldCoords ) {
	std::vector< GamePhysicObject* > *objs = mGameEngine->getPhysicObjectEngine()->getPhysicObjects();
	// TODO: fix algorithm to grab the nearest bomb instead of the one found first
	for( unsigned int i = 0; i < objs->size(); ++i ) {
		if( objs->at( i )->getObjectType() > ELEMENT_INVALID && objs->at( i )->calculateDistanceTo( worldCoords ) < 2.0f )
			return objs->at( i );
	}
	return 0;
}
